/*
 * Copyright (c) 2023-2023 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.module.dict.api.aop;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.elsfs.cloud.common.annotations.Dict;
import org.elsfs.cloud.common.core.utils.SpringContextHolder;
import org.elsfs.cloud.common.core.vo.R;
import org.elsfs.cloud.common.util.lang.StringUtils;
import org.elsfs.cloud.module.dict.api.service.DictService;

/**
 * 执行翻译
 *
 * @see org.springframework.aop.Advisor
 * @see org.elsfs.cloud.common.core.aop.AnnotationAdvisor
 * @author zeng
 */
@Slf4j
@RequiredArgsConstructor
public class DictMethodInterceptor implements MethodInterceptor {
  /** DictService的保护级别的最终成员变量。 这是一个字典服务，用于在类中进行字典相关的操作。 */
  protected final DictService getDictService() {
    return SpringContextHolder.getBean(DictService.class);
  }

  @Override
  public Object invoke(MethodInvocation invocation) throws Throwable {
    long start = System.currentTimeMillis();
    Object returnVar = invocation.proceed();
    if (ignoreType(returnVar)) {
      return returnVar;
    }
    LOGGER.debug("运行时间：{}毫秒", System.currentTimeMillis() - start);
    parseResult(returnVar);
    return returnVar;
  }

  /**
   * 翻译返回结果
   *
   * @param result result
   */
  public void parseResult(Object result) {
    // 判断结果类型
    if (result instanceof List<?> resultList) {
      resultList.forEach(this::parseDict);
    }
    if (result instanceof R<?> r) {
      parseResult(r.getResult());
    }
    if (result instanceof IPage<?> page) {
      parseResult(page.getRecords());
    }
    parseDict(result);
  }

  /**
   * 解析带有Dict注解的对象字典。 该方法通过反射，获取对象类中所有带有Dict注解的字段，并对这些字段进行处理。
   *
   * @param obj 需要解析的对象。
   */
  private void parseDict(Object obj) {
    // 使用反射缓存获取对象类的所有带有Dict注解的字段
    var annotatedFields = ReflectionCache.getAnnotatedFields(obj.getClass());
    annotatedFields.forEach(field -> handleDict(obj, field));
  }

  /**
   * 处理字典映射，将一个字段的值映射到另一个字段上。
   *
   * @param obj 要处理的对象实例。
   * @param field 需要被映射的字段。
   */
  private void handleDict(Object obj, Field field) {
    try {
      var dict = field.getAnnotation(Dict.class);
      var target = dict.target(); // 目标字段名
      var targetField = ReflectionCache.getCachedDeclaredField(obj.getClass(), target);
      if (targetField == null) {
        return;
      }
      // 允许当前字段和目标字段可访问
      field.setAccessible(true);
      var key = field.get(obj);
      if (ObjectUtils.isEmpty(key)) {
        return;
      }
      if (StringUtils.isBlank(key.toString())) {
        return;
      }
      targetField.setAccessible(true);
      // 根据字典类型和当前字段的值，更新目标字段的值
      var dictItemEntities = getDictService().getListByDictCode(dict.dictCode());
      for (var item : dictItemEntities) {
        if (Objects.equals(item.getLabel(), key.toString())) {
          targetField.set(obj, item.getValue());
        }
      }
      // 重置字段访问权限
      targetField.setAccessible(false);
      field.setAccessible(false);
    } catch (IllegalAccessException e) {
      LOGGER.error("Failed to handle dictionary field: {}", field.getName(), e);
    }
  }

  private boolean ignoreType(Object returnVar) {
    return returnVar == null || returnVar instanceof CharSequence || returnVar instanceof Number;
  }
}
